<?php
/**
 * @file app.class.php
 * @author 禅元天道 chanyuantiandao@126.com
 * @DateTime 2022-01-17 15:03
 * @brief 网站app类，负责地址分发
 */

!defined('CHAN_CMS') && exit('非法访问！');
class App{

    private static $config = null;
    private static $log = null;
    private static $cache = null;
    private static $sqlcmd = null;
    private static $supportedDBArrays = array('mysql');

    private static function init(){

        if (function_exists('date_default_timezone_set')) {
            date_default_timezone_set('Asia/Shanghai'); // 默认是北京时间
        }
        self::$config = array(
            'autoload' => array(
                'source/controller/',
                'source/class/helper/',
                'source/class/thirdparty/'
            ),
            'site' => array(
                'name' => 'CAE文档',
                'keywords' => '',
                'description' => '',
                'url' => 'http://www.caedoc.com/',
                'encrypt_key' => 'DO NOT ASK, JUST TRY IT!',
                'noAccessHeader' => '<?php !defined(\'CHAN_CMS\') && exit(); ?>'
            )
        );
        $config = array();
        if(!include(FileHelper::joinPathWithRoot('source','config').'common.config.php')){
            throw new ChanException('读取配置文件common.config.php失败！');
        }
        self::$config = array_merge_recursive(self::$config, $config);

        $dbDriveFile = FileHelper::joinPathWithRoot('source','class','database').strtolower(self::$config['database']['type']).'.class.php';
        if(in_array(strtolower(self::$config['database']['type']), self::$supportedDBArrays) && file_exists($dbDriveFile)){
            require_once($dbDriveFile);
            self::$sqlcmd = DB::getInstance(self::$config['database']);
        }else{
            throw new ChanException('未能识别的数据库类型：'.self::$config['database']['type']);
        }

        self::$log = Log::getInstance(self::$config['log']);
        self::$cache = Cache::getInstance(self::$config['cache']);

    }

    public static function run(){
        if(self::$config == null){
            self::init();
        }
        $vars = array_merge(self::$config['default'], $_GET);
        if($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST)) {
            $vars = array_merge($vars, $_POST);
        }

        if(isset(self::$config['url']['rewrite']) && self::$config['url']['rewrite']){
            if(isset($_SERVER['PATH_INFO'])){
                $vars = array_merge($vars, self::parseParams($_SERVER['PATH_INFO']));
            }
        }

        $key = md5(serialize($vars));
        if($_SERVER['REQUEST_METHOD'] == 'GET'){
            $htmlContent = App::getCache($key, Cache::HTML);
            if($htmlContent != null){
                echo $htmlContent;
                exit;
            }else{
                ob_start();
                self::dispatch($vars);
                self::setCache($key, compressCode(ob_get_contents(), 'html'), Cache::HTML);
            }
        }else{
            self::dispatch($vars);
        }


    }

    public static function db(){
        return self::$sqlcmd;
    }

    public static function log(string $message){
        self::$log->info($message);
    }

    public static function getPath(string $key, int $type){
        return self::$cache->getPath($key, $type);
    }

    public static function getCache(string $key, int $type){
        return self::$cache->get($key, $type);
    }

    public static function setCache(string $key, string $data, int $type){
        return self::$cache->set($key, $data, $type);
    }

    public static function removeCacheByKey(string $key, int $type){
        return self::$cache->remove($key, $type);
    }

    public static function removeCacheByFlag(string $flag, int $type){
        return self::$cache->removeAll($flag, $type);
    }

    public static function config(string $key = '', $default = null){
        if(empty($key)) return $default;
        $keys = explode('/', trim($key, '/'));
        $val = self::$config;
        foreach ($keys as $k){
            if(isset($val[$k])){
                $val = $val[$k];
            }else{
                $val = $default;
                break;
            }
        }
        return $val;
    }

    public static function parseParams($QUERY_STRING)
    {
        return array();
    }

    private static function dispatch($params)
    {
        $className = ucfirst($params['mod'].'Controller');
        $methodName = $params['act'];
        if(class_exists($className, true)){
            $reflectionClass = new ReflectionClass($className);
            $args = $missing = array();
            if($reflectionClass->hasMethod($methodName)){
                $reflectionMethod = new ReflectionMethod($className, $methodName);
                foreach ($reflectionMethod->getParameters() as $param){
                        $name = $param->getName();//获取参数名
                        //$params　参数存在于　传入的参数之中
                        if (array_key_exists($name, $params)) {  //有传参数，　按传入的参数
                            $temp = null;
                            switch ($param->getType()){
                                case 'boolean':
                                    $temp = getBooleanVal($params[$name], null);
                                    break;
                                case 'int':
                                case 'integer':
                                    //官方文档说明返回integer，但实际测试会返回int
                                    $temp = getIntVal($params[$name], null);
                                    break;
                                case 'double':
                                    $temp = getNumber($params[$name], null);
                                    break;
                                case 'string':
                                case null:
                                    //函数定义时候未指定参数类型的，默认当作字符串处理
                                    $temp = getPureString($params[$name], null);
                                    break;
                                default:
                                    echo '$param->getType()';
                                    break;
                            }
                            if($temp == null){
                                throw new ChanException('未给'.$className.'->'.$methodName.'()提供合适的函数参数！');
                            }else{
                                $args[] = $temp;
                            }
                            unset($temp);
                            unset($params[$name]);
                        } elseif ($param->isDefaultValueAvailable()) {  //没有传参数，　检测时候参数有默认值
                            //getDefaultValue 获取参数默认值
                            $args[] = $param->getDefaultValue();
                        } else {
                            $missing[] = $name;
                            break;
                        }
                }
                if (!empty($missing)){
                    throw new ChanException('在执行'.$className.'->'.$methodName.'()时丢失参数：'.implode(',', $missing).'!');
                }else{
                    $obj = new $className;
                    $obj->_before();
                    $obj->{$methodName}(implode(',', $args));
                    $obj->_after();
                    /*
                    $method = array(new $className, $methodName);
                    if (is_callable($method)) {
                        call_user_func_array($method,$args);
                    }*/
                }
            }else{
                throw new ChanException('在类'.$className.'中没有方法'.$methodName.'()!');
            }
        }else{
            throw new ChanException('未找到类'.$className.'，请确认参数是否正确！');
        }
    }

    public static function shutdown_handler(){
        $last_error = error_get_last();
        print_r($last_error);
    }

    public static function exception_handler($exception){

        $trace = $exception->getTrace();
        echo '<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><title>'.App::$config['site']['name'].' - 异常报告</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /><style>body { background-color: white; color: black; font: 9pt/11pt verdana, arial, sans-serif;}#container { width: 1024px; }.red  {color: red;}a{ font: 9pt/11pt verdana, arial, sans-serif; color: red; }h1 { color: #FF0000; font: 18pt "Verdana"; margin-bottom: 0.5em;}.bg1{ background-color: #FFFFCC;}.bg2{ background-color: #EEEEEE;}.table {background: #AAAAAA; font: 11pt Menlo,Consolas,"Lucida Console"}.info {background: none repeat scroll 0 0 #F3F3F3;  border: 0 solid #aaaaaa; border-radius: 10px; color: #000000; font-size: 11pt; line-height:160%; margin-bottom: 1em; padding: 1em;}.help {background: #F3F3F3; border-radius: 10px; font: 12px verdana, arial, sans-serif; text-align: center; line-height: 160%; padding: 1em;}</style></head><body><div id="container"><h1>异常报告</h1><div class="info">[Exception]:'.$exception->getMessage().'</div><div class="info"><p><strong>PHP Debug</strong></p><table cellpadding="5" cellspacing="1" width="100%" class="table"><tr class="bg2"><td>No.</td><td>File</td><td>Line</td><td>Code</td></tr>';

        $index = sizeof($trace);
        $no = 2;
        $method = '';

        echo '<tr class="bg1"><td>'.$no.'</td><td>'.$exception->getFile().'</td><td>'.$exception->getLine().'</td><td>'.$method.$trace[0]['function'].'()</td></tr>';
        for ($i = 1; $i < $index; $i++, $no++){
            if(isset($trace[$i]['type']) && $trace[$i]['type'] == '::'){
                $method = 'Object::';
            }else if(isset($trace[$i]['type']) && $trace[$i]['type'] == '->'){
                $method = '$object->';
            }
            echo '<tr class="bg1"><td>'.$no.'</td><td>'.$trace[$i]['file'].'</td><td>'.$trace[$i]['line'].'</td><td>'.$method.$trace[$i]['function'].'()</td></tr>';
        }
        echo '</table></div><div class="help"><a href="'.App::$config['site']['url'].'">'.App::$config['site']['name'].'</a> 已经将此出错信息详细记录, 由此给您带来的访问不便我们深感歉意. <a href="http://www.yanzhihui.com/" target="_blank"><span class="red">Need Help?</span></a></div></div></body></html>';
        exit;
    }

    public static function error_handler($errno, $errstr, $errfile, $errline){
        echo 'No: '. $errno. '<br />str: '.$errstr. '<br />file: '.$errfile. '<br />line: '.$errline;
        exit;
    }
}